package cn.b504.qpcr.impls.cal;

import cn.b504.qpcr.entity.*;
import cn.b504.qpcr.interfaces.cal.Cal;
import cn.b504.qpcr.interfaces.cal.CalCq;
import cn.b504.qpcr.interfaces.cal.Calibrate;
import cn.b504.qpcr.utils.CalCqUtils;
import cn.b504.qpcr.utils.MathUtils;
import cn.b504.qpcr.utils.Num;
import cn.b504.qpcr.utils.Utils;
import ij.measure.CurveFitter;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Future;

/**
 * @author ssc
 */
@Service
public class CalImpl implements Cal {
  private final Calibrate calibrate;
  private final CalCq calCq;

  public CalImpl(Calibrate calibrate, CalCq calCq) {
    this.calibrate = calibrate;
    this.calCq = calCq;
  }

  @Override
  public void calibrate(Data data) {
    // 初始化 opt 对象：从 origin 中复制一份过来
    data.getWells().forEach(well -> Arrays.stream(well.getLines()).forEach(it -> it.setQuantificationOpt(it.copyQuantificationOrigin())));
    // 取出全部的校准系数
    final List<Coefficient> coefficients = data.getCoefficients();
    // 如果只有一条，甚至没有校准系数，那么就不校准
    if (coefficients.size() < 2) {
      return;
    }
    final List<Future<?>> list = new ArrayList<>();
    data.getWells().forEach(it -> list.add(calibrate.calibrate(it, coefficients)));
    Utils.ensureEmptyFutureList(list);
  }

  //calStartEndCycle备份
   /* public void calStartEndCycle(Data data) {
      AnalysisMethodSetting setting = data.getAnalysisMethodSetting();
      data.getWells().forEach(it -> {
        Well.Line[] lines = it.getLines();
        for (Well.Line line : lines) {
          List<Float> list = CalCqUtils.getData(line, AnalysisMethodSetting.Method.RAW_DATA);
          if (list.size() == 0) {
            continue;
          }
          float cqValue = CalCqUtils.calByEarlyExtremum(list);
          if (cqValue < 6) {
            line.setBaseStartCycle(1);
            line.setBaseEndCycle(3);
          } else {
            line.setBaseStartCycle(3);
            line.setBaseEndCycle((int) (cqValue - 2));
          }
        }
      });
    }*/
//  @Override
//重新计算起始终止循环0908/2022
  /*public void calStartEndCycle(Data data) {
      data.getWells().forEach(it -> {
        Well.Line[] lines = it.getLines();
        for (Well.Line line : lines) {
          List<Float> list = CalCqUtils.getData(line, AnalysisMethodSetting.Method.RAW_DATA);
          if (list.size() == 0) {
            continue;
          }
          List<Float> gradList = new ArrayList<>(list);
          int maxIndex = 0;
          float gradMax = -Float.MAX_VALUE;
          float max = -Float.MAX_VALUE;
          for (int i = gradList.size() - 2; i >= 0; i--) {
            if (gradList.get(i) <= 0) {
              gradList.set(i, gradList.get(i + 1));
            }
          }
          for (int i = 0; i < gradList.size() - 2; i++) {
            gradList.set(i, (gradList.get(i) + gradList.get(i + 1) + gradList.get(i + 2)) / 3);
          }
          for (Float aFloat : gradList) {
            if (aFloat > max) {
              max = aFloat;
            }
          }
          for (int i = 0; i < gradList.size() - 1; i++) {
            gradList.set(i, (gradList.get(i + 1) - gradList.get(i)) / max);
          }
          gradList.set(gradList.size() - 1, (gradList.get(gradList.size() - 2)));
          for (int i = 0; i < gradList.size(); i++) {
            if (gradList.get(i) > gradMax) {
              gradMax = gradList.get(i);
              maxIndex = i;
            }
          }
          if (maxIndex < 6) {
            gradList.set(maxIndex, 0.00F);
            for (int i = 0; i < gradList.size(); i++) {
              if (gradList.get(i) > gradMax) {
                gradMax = gradList.get(i);
                maxIndex = i;
              }
            }
          }
          if (maxIndex >= 20) {
            line.setBaseStartCycle(maxIndex - 15);
            line.setBaseEndCycle(maxIndex - 6);
          } else if (maxIndex <= 5) {
            line.setBaseStartCycle(1);
            line.setBaseEndCycle(4);
          } else if (maxIndex <= 10) {
            line.setBaseStartCycle(maxIndex - 5);
            line.setBaseEndCycle(maxIndex - 3);
          } else {
            line.setBaseStartCycle(maxIndex - 8); //10-20
            line.setBaseEndCycle(maxIndex - 3);
          }
        }
      });
  }*/
  /*public void calStartEndCycle(Data data) {
    data.getWells().forEach(it -> {
      Well.Line[] lines = it.getLines();
      for (Well.Line line : lines) {
        List<Float> list = CalCqUtils.getData(line, AnalysisMethodSetting.Method.RAW_DATA);
        if (list.size() == 0) {
          continue;
        }
        List<Float> gradList = new ArrayList<>(list);
        int maxIndex = 0;
        float gradMax = -Float.MAX_VALUE;
        float max = -Float.MAX_VALUE;
        for (int i = gradList.size() - 2; i >= 0; i--) {
          if (gradList.get(i) <= 0) {
            gradList.set(i, gradList.get(i + 1));
          }
        }
        for (int i = 0; i < gradList.size() - 2; i++) {
          gradList.set(i, (gradList.get(i) + gradList.get(i + 1) + gradList.get(i + 2)) / 3);
        }
        for (Float aFloat : gradList) {
          if (aFloat > max) {
            max = aFloat;
          }
        }
        for (int i = 0; i < gradList.size() - 1; i++) {
          gradList.set(i, (gradList.get(i + 1) - gradList.get(i)) / max);
        }
        gradList.set(gradList.size() - 1, (gradList.get(gradList.size() - 2)));
        for (int i = 0; i < gradList.size(); i++) {
          if (gradList.get(i) > gradMax) {
            gradMax = gradList.get(i);
            maxIndex = i;
          }
        }
        if (maxIndex < 6) {
          gradList.set(maxIndex, 0.00F);
          for (int i = 0; i < gradList.size(); i++) {
            if (gradList.get(i) > gradMax) {
              gradMax = gradList.get(i);
              maxIndex = i;
            }
          }
        }
        if (maxIndex >= 20) {
          line.setBaseStartCycle(maxIndex - 15);
          line.setBaseEndCycle(maxIndex - 6);
        } else if (maxIndex <= 5) {
          line.setBaseStartCycle(1);
          line.setBaseEndCycle(4);
        } else if (maxIndex <= 10) {
          line.setBaseStartCycle(maxIndex - 5);
          line.setBaseEndCycle(maxIndex - 3);
        } else {
          line.setBaseStartCycle(maxIndex - 8); //10-20
          line.setBaseEndCycle(maxIndex - 3);
        }
      }
    });
  }*/
  //重新计算起始循环数0911/2022
  public void calStartEndCycle(Data data) {
    data.getWells().forEach(it -> {
      Well.Line[] lines = it.getLines();

      for (Well.Line line : lines) {
        List<Float> list = CalCqUtils.getData(line, AnalysisMethodSetting.Method.RAW_DATA);
        if (list.size() == 0) {
          continue;
        }
        List<Float> gradList = new ArrayList<>(list);
        List<Float> origin = new ArrayList<>(list);    //原始数组拷贝
        List<Float> filterList = new ArrayList<>(list);//接收进行均值处理的数组
        //0913先注释
//        List<Float> beforeFitList = new ArrayList<>(list);
//        QuantificationHelper.curveFitter_4p(gradList, beforeFitList);
        int maxIndex = 0, minIndex = 0, cqInit = -1;
        float gradMax = -Float.MAX_VALUE;
        int max_20_Index = 0;
        float max = -Float.MAX_VALUE, min = Float.MAX_VALUE;
    /* 0914
    for (int i = gradList.size() - 2; i >= 0; i--) {
          if (gradList.get(i) <= 0) {
            gradList.set(i, gradList.get(i + 1));
          }
        }*/
        filterList.set(0, origin.get(0));
        filterList.set(origin.size() - 1, origin.get(origin.size() - 1));

        for (int i = 1; i < gradList.size() - 1; i++) {
          //平滑处理
          filterList.set(i, (float) (origin.get(i - 1) * 0.25 + origin.get(i) * 0.5 + origin.get(i + 1) * 0.25));
        }
//        filterList= CalCqUtils.averageFilter2NewList(origin); //均值滤波
       /*0914
       for (Float aFloat : gradList) {
          if (aFloat > max) {
            max = aFloat;
          }
        }*/
        for (int i = 0; i < filterList.size(); i++) {
          //均值处理后的数组求最小值及索引
          if (filterList.get(i) < min) {
            min = filterList.get(i);
            minIndex = i;
          }
        }
        for (int i = 0; i < filterList.size() - 1; i++) {
          //计算坡度数组
          gradList.set(i, (filterList.get(i + 1) - filterList.get(i)));
        }
        gradList.set(gradList.size() - 1, (gradList.get(gradList.size() - 2))); //最后一个值计算坡度

        for (int i = minIndex; i < gradList.size(); i++) {
          //最小值处遍历找坡度最大值及索引
          if (gradList.get(i) > gradMax) {
            gradMax = gradList.get(i);
            maxIndex = i;
          }
        }
        float max_20 = (float) (gradList.get(maxIndex) * 0.2);
        for (int i = maxIndex; i > 1; i--) {
          //找坡度最大值20%索引
          if (gradList.get(i) >= max_20 && gradList.get(i - 1) < max_20) {
            max_20_Index = i;
            break;
          }
        }

        //0914上午加入cq判断标准
      /*  if (maxIndex >= 5) {
          if ((gradList.get(maxIndex) - gradList.get(maxIndex - 1)) > 0
              *//*(gradList.get(maxIndex - 1) - gradList.get(maxIndex - 2)) &&
              gradList.get(maxIndex - 2) > 0 && (gradList.get(maxIndex - 1) - gradList.get(maxIndex - 2)) > 0*//*) {

            cqInit = maxIndex - 2;
          } else {
            cqInit = -1;
          }
        } else {
          cqInit = maxIndex - 2;
        }*/
//        System.out.println("cqInit = " + cqInit + "  maxIndex = " + maxIndex);
        //根据cqInit设置起始循环数
        if (max_20_Index <= 1) {
          line.setBaseStartCycle(3);
          line.setBaseEndCycle(5);
        } else {
          line.setBaseEndCycle(max_20_Index - 1);
          if ((max_20_Index - 1) >= 15) {
            line.setBaseStartCycle(max_20_Index - 13);
          } else {
            line.setBaseStartCycle(1);
          }
        }



      /*  if (cqInit > 20) {
          line.setBaseStartCycle(cqInit - 14);
//          line.setBaseEndCycle(cqInit - 2);
        } else if (cqInit < 5) {
          line.setBaseStartCycle(1);
//          line.setBaseEndCycle(3);
        } else {   //cq:5-20
          line.setBaseStartCycle(3);
//          line.setBaseEndCycle(cqInit);
        }*/


        /*0914
        if (gradList.size() < 10) {
          line.setBaseStartCycle(3);
          line.setBaseEndCycle(gradList.size() - 1);
        } else if (gradList.size() > 20) {
          if (gradMax < 0.02 || maxIndex <= 5) {
            line.setBaseStartCycle(10);
            line.setBaseEndCycle(20);
          } else {
            if (maxIndex >= 20) {
              line.setBaseStartCycle(maxIndex - 15);
              line.setBaseEndCycle(maxIndex - 6);
            } else if (maxIndex <= 10) {  //5-10
              line.setBaseStartCycle(maxIndex - 5);
              line.setBaseEndCycle(maxIndex - 3);
            } else {
              line.setBaseStartCycle(5); //10-20
              line.setBaseEndCycle(maxIndex - 3);
            }
          }
        } else {
          //size===>10-20
          if (gradMax < 0.02 || maxIndex <= 5) {
            line.setBaseStartCycle(10);
            line.setBaseEndCycle(17);
          } else {
            if (maxIndex <= 10) {  //5-10
              line.setBaseStartCycle(maxIndex - 5);
              line.setBaseEndCycle(maxIndex - 3);
            } else {
              line.setBaseStartCycle(5); //10-20
              line.setBaseEndCycle(maxIndex - 3);
            }
          }
        }*/
      }
    });
  }

  @Override
  public void calCq(Data data) {
    AnalysisMethodSetting setting = data.getAnalysisMethodSetting();
    final List<Future<?>> list = new ArrayList<>();
    final String method = setting.getMethod();
    final int startCycle = setting.getStartCycle();
    final int endCycle = setting.getEndCycle();
    data.getWells().forEach(well -> Arrays.stream(well.getLines()).forEach(it -> it.setCq(-1)));  //初始化操作,所有well李线的的数据cqValue设置为-1
    switch (setting.getMode()) {
      case AnalysisMethodSetting.Mode.CUSTOM_THRESHOLD:
        // [warning] 警告：一定要保证每个孔里的 target
        // s 的 channelIndex 不重复，否则多线程处理会使数据不安全
        data.getTargets().forEach(it -> list.add(calCq.calByCustomThreshold(it, data, method)));
        break;
      case AnalysisMethodSetting.Mode.THRESHOLD:
        data.getTargets().forEach(it -> it.setThreshold((float) 0.01));
        // [warning] 警告：一定要保证每个孔里的 targets 的 channelIndex 不重复，否则多线程处理会使数据不安全
        data.getTargets().forEach(it ->
            list.add(calCq.calByThreshold(it, data, startCycle, endCycle, method)
            ));
        break;
      case AnalysisMethodSetting.Mode.REGRESSION:
        data.getWells().forEach(it -> {
          Well.Line[] lines = it.getLines();
          for (Well.Line line : lines) {
            if (line.getOmit()) {
              continue;
            }
            //  TODO:改了循环
//            list.add(calCq.calByRegression(line, startCycle, endCycle, method));

            int start = line.getBaseStartCycle();
            int end = line.getBaseEndCycle();
            list.add(calCq.calByRegression(line, start, end, method));
          }
        });
        break;
      case AnalysisMethodSetting.Mode.EXTREMUM:
        data.getWells().forEach(it -> {
          Well.Line[] lines = it.getLines();
          for (Well.Line line : lines) {
            line.setCq(-1);
            if (line.getOmit()) {
              continue;
            }
            list.add(calCq.calByExtremum(line, method));
          }
        });
        break;
      case AnalysisMethodSetting.Mode.CQMAN:
        calCq.calByCqMan(data, method);
        break;
      default:
    }
    Utils.ensureEmptyFutureList(list);
  }

  @Override
  public Future<?> calSq(final PlateSetting.Target target, final Data data) {
    final short channelIndex = target.getChannelIndex();
    final Num num = new Num();
    // 对 std 及正确 cq，计算 a, b, c, d, n
    data.getWells().forEach(well -> {
      float sq = well.getLines()[channelIndex].getSq();
      if (sq <= 0) {
        return;
      }
      Float cq = SqHelper.getCq(well, target.getName(), channelIndex, true);
      if (cq == null || cq == -1) {
        return;
      }
      double logSQ = Math.log10(sq);
      num.a += logSQ * cq;
      num.b += logSQ;
      num.c += cq;
      num.d += Math.pow(logSQ, 2);
      num.n++;
    });
    // 计算直线拟合的 k, b
    final double[] kb = SqHelper.getKB(num);
    double cAverage = num.c / num.n;
    // 计算直线拟合的效率 E 和 R^2
    data.getWells().forEach(well -> {
      float sq = well.getLines()[channelIndex].getSq();
      if (sq <= 0) {
        return;
      }
      Float cq = SqHelper.getCq(well, target.getName(), channelIndex, true);
      if (cq == null || cq == -1) {
        return;
      }
      num.r += Math.pow((kb[0] * Math.log10(sq) + kb[1] - cAverage), 2);  // ssr
      num.t += Math.pow((cq - cAverage), 2);  // sst
    });
    target.setK(kb[0]);
    target.setB(kb[1]);
    target.setE(Math.pow(10, -1 / kb[0]) - 1);
    target.setR2(num.t == 0 ? 0 : num.r / num.t);
    // 计算 Sq
    data.getWells().forEach(well -> {
      Float cq = SqHelper.getCq(well, target.getName(), channelIndex, false);
      if (cq == null) {
        // 有可能是 omit 的、也有可能是 std 的，做跳过处理
        return;
      }
      if (cq == -1) {
        well.getLines()[channelIndex].setSq(-1);
        return;
      }
      double sq = Math.pow(10, (cq - kb[1]) / kb[0]);
      well.getLines()[channelIndex].setSq(Double.isNaN(sq) ? -1 : (float) sq);
    });
    return new AsyncResult<>(null);
  }

  static class SqHelper {
    private static Float getCq(Well well, final String targetName, short channelIndex, boolean isStd) {
      if (CalCqUtils.shouldSkip(well, targetName, channelIndex)) {
        return null;
      }
      if (well.getType().equals(Well.STD)) {
        if (!isStd) {
          return null;
        }
      } else {
        if (isStd) {
          return null;
        }
      }
      return well.getLines()[channelIndex].getCq();
    }

    private static double[] getKB(Num num) {
      double x, y, k, b;
      x = num.n * num.a - num.b * num.c;
      y = num.n * num.d - Math.pow(num.b, 2);
      k = x / y;
      b = num.c / num.n - k * num.b / num.n;
      return new double[]{k, b};
    }
  }

  @Override
  @Async
  public Future<?> calMeltPeakAndMeltTemperature(final Data data) {
    Optional<Protocol.Stage> stage = data.getProtocol().getStages().stream()
        .filter(it -> it.getState().equals(Protocol.Stage.MELT)).findAny();
    final float startTemp;
    final float gradient;
    if (stage.isPresent()) {
      Protocol.Stage.Step step = stage.get().getSteps().get(0);
      startTemp = step.getTemperature();
      gradient = step.getGradient();
    } else {
      return new AsyncResult<>(null);
    }

    data.getWells().forEach(it -> {
      if (it.getTargets().size() == 0) {
        return;
      }
      // 先对原始数据做滤波处理
      List<Float> list = MeltHelper.preprocess(new ArrayList<>(it.getMeltData()));
      // 计算 melt peak 曲线
      List<Float> derivativeList = MathUtils.oppositeNumber(MathUtils.getDaoList_baseOn3(list));
      it.setMeltPeak(derivativeList);
      // 计算 melt peak 峰值对应温度
      it.setMeltTemperature(startTemp + gradient * MathUtils.maxIndexSinceIndex3(derivativeList));
      // 对 melt peak 曲线起峰之前的数据做滤波处理
      if (MeltHelper.checkNonStraightLine(derivativeList)) {
        MeltHelper.averageFilter(derivativeList);
      }
    });

    data.getTargets().forEach(it -> {
      it.setMeltThreshold(-1);
      Num num = new Num();
      data.getWells().forEach(well -> {
        if (CalCqUtils.shouldSkip(well, it.getName(), it.getChannelIndex())) {
          return;
        }
        if (well.getMeltTemperature() == -1) {
          return;
        }
        num.y += well.getMeltPeak().get((int) ((well.getMeltTemperature() - startTemp) / gradient));
        num.i++;
      });
      it.setMeltThreshold((float) (num.y / num.i));
    });
    return new AsyncResult<>(null);
  }

  static class MeltHelper {

    // [warning] 警告：该方法会对入参对象元素直接修改
    private static List<Float> preprocess(List<Float> meltData) {
      final int size = meltData.size();
      if (size > 4) {
        for (int i = 0; i < size - 1; i++) {
          if (i == size - 2) {
            meltData.set(i, (meltData.get(i) + meltData.get(i + 1) + meltData.get(i + 1) + meltData.get(i + 1) + meltData.get(i + 1)) / 5);
          } else if (i == size - 3) {
            meltData.set(i, (meltData.get(i) + meltData.get(i + 1) + meltData.get(i + 2) + meltData.get(i + 2) + meltData.get(i + 2)) / 5);
          } else if (i == size - 4) {
            meltData.set(i, (meltData.get(i) + meltData.get(i + 1) + meltData.get(i + 2) + meltData.get(i + 3) + meltData.get(i + 3)) / 5);
          } else {
            meltData.set(i, (meltData.get(i) + meltData.get(i + 1) + meltData.get(i + 2) + meltData.get(i + 3) + meltData.get(i + 4)) / 5);
          }
        }
      }
      return meltData;
    }

    // 检查是否是 非直线
    private static boolean checkNonStraightLine(List<Float> list) {
      int sizeHalf = list.size() / 2;
      if (list.size() > 8) {
        float sum3_7 = (list.get(0) + list.get(1) + list.get(2) + list.get(3) + list.get(4));
        float sumLast5 = (list.get(sizeHalf) + list.get(sizeHalf + 1) + list.get(sizeHalf + 2) + list.get(sizeHalf + 3) + list.get(sizeHalf + 4));
        if (sumLast5 / 5 - 0.3 < 0) {
          return false;
        } else {
          return Math.abs(sumLast5) - Math.abs(sum3_7) * 2 >= 0;
        }
      } else {
        return false;
      }
    }

    // 对突增点之前的部分做均值滤波
    private static void averageFilter(List<Float> list) {
      // 找突增点
      float cq = CalCqUtils.getCqByMaximum(list);
      if (cq < CalCqUtils.cqMinLimit || list.size() < cq) {
        return;
      }
      // 找突增点对应的 y 值
      float y = MathUtils.x2y(list, cq);
      for (int i = list.size() - 1; i > 0; i--) {
        float left = list.get(i - 1), right = list.get(i);
        if (left <= y && y <= right) {
          float cqFinal = i - (y - right) / (left - right);
          for (int j = 0; j < cqFinal; j++) {
            // TODO: 这个算法可能有点毛病，对 0 - cqFinal 范围内的 list 反复做了 cqFinal 次均值滤波？
            CalCqUtils.averageFilter(list.subList(0, (int) Math.floor(cqFinal)));
          }
        }
      }
    }
  }

  @Override
  @Async
  public Future<?> calQuantificationShow(Well.Line line) {//单条线的本底去除、拟合
    List<Float> listAfterProcess = QuantificationHelper.baseProcess(line.getQuantificationOpt(), line.getBaseStartCycle(), line.getBaseEndCycle(), line.getSigLevel());
    line.setQuantificationBaseline(listAfterProcess);  //设置本底去除数据
    List<Float> listAfterCurveFitter = QuantificationHelper.curveFitter(listAfterProcess);
    line.setQuantificationCurveFitter(listAfterCurveFitter);  //设置拟合数据
    return new AsyncResult<>(null);
  }

  static class QuantificationHelper {

    /*private static List<Float> baseProcess(List<Float> list, int startCycle, int endCycle) {
      List<Float> result = new ArrayList<>(list);
      int count = 0;
      double sum = 0;
      for (int i = Math.max(startCycle - 1, 0); i < Math.min(endCycle, result.size()); i++) {
        sum += result.get(i);
        count++;
      }
      float ave = (float) (sum / count);
      for (int i = 0; i < result.size(); i++) {
        result.set(i, result.get(i) - ave);
      }
      return result;
    }*/

    /*private static List<Float> curveFitter(List<Float> list) {
      List<Float> result = new ArrayList<>(list);
      int size = result.size();

      boolean error;
      if (result.size() < 5) {
        error = true;
      } else {
        float avePre = (result.get(0) + result.get(1) + result.get(2) + result.get(3) + result.get(4)) / 5F;
        float avePost = (result.get(result.size() - 1) + result.get(result.size() - 2) + result.get(result.size() - 3)) / 3F;
        error = (avePost - avePre) / avePre < 0.2;
      }
      if (error) {
        float sumPre = 0, sumPost = 0; // 前 3 个、后 3 个求和
        for (int i = 0; i < Math.min(3, size); i++) {
          sumPre += result.get(i);
          sumPost += result.get(size - i - 1);
        }
        // 前后 3 个比大小
        if (sumPre > sumPost) {
          for (int i = 0; i < size; i++) {
            result.set(i, 0.01F);
          }
        } else {
          float min = MathUtils.getMin(result);
          for (int i = 0; i < size; i++) {
            result.set(i, (result.get(i) - min) + 0.01F);  // 减去最小值
          }
        }
        return result;
      }

      curveFitter_ykxb(result);
      float min = curveFitter_4p(result);

      // 前 3 个、后 3 个求和
      float sumPre = 0, sumPost = 0;
      for (int i = 0; i < 3; i++) {
        sumPre += result.get(i);
        sumPost += result.get(size - i - 1);
      }
      // 前后 3 个比大小
      if (sumPre > sumPost) {
        for (int i = 0; i < size; i++) {
          result.set(i, 0.01F);
        }
      } else {
        for (int i = 0; i < size; i++) {
          result.set(i, (result.get(i) - min) + 0.01F);  // 减去最小值
        }
      }
      return result;
    }
*/

    //  TODO:本地去除备份
/*    private static List<Float> baseProcess(List<Float> list, int startCycle, int endCycle) {
      List<Float> result = new ArrayList<>(list);
      int size = result.size();

      boolean error;
      if (result.size() < 5) {
        error = true;
      } else {
        float preMin = result.get(0);
        float postMax = result.get(result.size() - 1);
        for (int i = 0; i < 10; i++) {
          preMin = Math.min(result.get(i), preMin); //  找到前10个数的最小值
        }
        for (int i = 0; i < 10; i++) {
          if (result.get(i) == preMin) {
            break;
          }
          result.set(i, preMin);  //  把最小值前面的数都设置成最小值
        }
        for (int i = 1; i <= 5; i++) {
          postMax = Math.max(result.get(result.size() - i), postMax); //  找到后5个数的最大值
        }
        error = Math.abs((postMax - preMin) / preMin) < 0.1;
      }

      if (error) {
        float sumPre = 0, sumPost = 0; // 前 3 个、后 3 个求和
        for (int i = 0; i < Math.min(3, size); i++) {
          sumPre += result.get(i);
          sumPost += result.get(size - i - 1);
        }
        // 前后 3 个比大小
        if (sumPre > sumPost) {
          for (int i = 0; i < size; i++) {
            result.set(i, 0.01F);
          }
        } else {
          float min = MathUtils.getMin(result);
          for (int i = 0; i < size; i++) {
            result.set(i, (result.get(i) - min) + 0.01F);  // 减去最小值
          }
        }
        return result;
      }

      if (error) {
        float sum = 0;
        for (Float aFloat : result) {
          sum += aFloat;
        }
        float ave = sum / size;
        for (int i = 0; i < size; i++) {
          result.set(i, result.get(i) - ave);
        }
        return result;
      }

      int count = 0;
      double sum = 0;
      for (int i = Math.max(startCycle - 1, 0); i < Math.min(endCycle, result.size()); i++) {
        sum += result.get(i);
        count++;
      }
      float ave = (float) (sum / count);
      for (int i = 0; i < result.size(); i++) {
        result.set(i, result.get(i) - ave);
      }
      return result;
    }*/

    //本地去除备份0911/2022
    private static List<Float> baseProcess(List<Float> list, int startCycle, int endCycle, float sigLevel) {
      List<Float> origin = new ArrayList<>(list);
      List<Float> result = new ArrayList<>(list);
      int size = result.size();
      if (size < endCycle - startCycle) {
        return result;
      }
      newCurveFitter(result, origin, startCycle, endCycle);
      if (CalCqUtils.isError(origin, sigLevel)) {
        for (int i = 0; i < size; i++) {
          result.set(i, (float) 0.001);
        }
        return result;
      }
      for (int i = 0; i < size; i++) {
        float value = origin.get(i) - result.get(i);
        if (value < 0.001) {
          value = (float) 0.001;
        }
        result.set(i, value);
      }
      //去除翘头
      for (int i = 0; i < startCycle; i++) {
        result.set(i, result.get(startCycle));
      }
      return result;
    }

    //新baseprocess方法
   /* private static List<Float> baseProcess(List<Float> list, int startCycle, int endCycle, float sigLevel) {
      List<Float> origin = new ArrayList<>(list);
      List<Float> result = new ArrayList<>(list);
      int size = result.size();
      if (size < endCycle - startCycle) {
        return result;
      }
      if (CalCqUtils.isError(origin, sigLevel)) {
        for (int i = 0; i < size; i++) {
          result.set(i, (float) 0.001);
        }
        return result;
      }
      newCurveFitter(result, origin, startCycle, endCycle);
      float minValue = Float.MAX_VALUE;
      int minIndex = 0;
      float average = 0f;
      for (int i = 0; i < result.size(); i++) {
        if (result.get(i) > minValue) {
          minValue = result.get(i);
          minIndex = i;
        }
      }
      if (minIndex >= 1 && minIndex < result.size() - 2) {
        average = (result.get(minIndex - 1) + result.get(minIndex) + result.get(minIndex + 1)) / 3;
      } else {
        average = minValue;
      }
      // 将拟合后的值拿出来更新
      for (int i = 0; i < result.size(); i++) {
        if ((result.get(i) - average) < 0) {
          result.set(i, 0.001F);
        } else {
          result.set(i, result.get(i) - average);  // curveFitter.f(xData[i]) 根据第 i 个 x 值求拟合后的 y 值
        }
      }
      return result;
    }*/

    private static List<Float> curveFitter(List<Float> list) {
      List<Float> result = new ArrayList<>(list);
      List<Float> origin = new ArrayList<>(list);
      int size = result.size();

//      curveFitter_ykxb(result);
      float min = curveFitter_4p(result, origin);

//      // 前 3 个、后 3 个求和
//      float sumPre = 0, sumPost = 0;
//      for (int i = 0; i < 3; i++) {
//        sumPre += result.get(i);
//        sumPost += result.get(size - i - 1);
//      }
//      // 前后 3 个比大小
//      if (sumPre > sumPost) {
//        for (int i = 0; i < size; i++) {
//          result.set(i, 0.01F);
//        }
//      } else {
//        for (int i = 0; i < size; i++) {
//          result.set(i, (result.get(i) - min) + 0.01F);  // 减去最小值
//        }
//      }
      return result;
    }

    // y = kx + b 拟合，减去 kx
    private static void curveFitter_ykxb(List<Float> result) {
      int minSize = Math.min(12, result.size());
      double[] xData0 = new double[minSize];  // x 坐标数组
      double[] yData0 = new double[minSize];  // y 坐标数组
      for (int i = 0; i < minSize; i++) {
        xData0[i] = i;
      }
      for (int i = 0; i < minSize; i++) {
        yData0[i] = result.get(i);
      }
      CurveFitter cf = new CurveFitter(xData0, yData0);    // 实例化
      cf.doCustomFit((p, x) -> p[0] + p[1] * x, 2, "y = a+bx", new double[]{0, 0}, null, false);
      double k = cf.getParams()[1];
      for (int i = 0; i < result.size(); i++) {
        result.set(i, (float) (result.get(i) - k * (i + 1)));  // curveFitter.f(xData[i]) 根据第 i 个 x 值求拟合后的 y 值
      }
    }

    //  新的拟合方法留底
  /*  private static void newCurveFitter(List<Float> result, List<Float> origin, int startCycle, int endCycle) {
      int size = result.size();
      double[] xData = new double[endCycle - startCycle];  // x 坐标数组
      double[] yData = new double[endCycle - startCycle];  // y 坐标数组
      for (int i = 0; i < endCycle - startCycle; i++) {
        xData[i] = i + startCycle; // x 坐标数组
      }
      for (int i = 0; i < endCycle - startCycle; i++) {
        yData[i] = result.get(i + startCycle); // y 坐标数组
      }
      CurveFitter curveFitter = new CurveFitter(xData, yData);    // 实例化
      curveFitter.doCustomFit((p, x) -> p[0] + (p[1] * x), 2, "y = a+bx", new double[]{0, 0}, null, false);

      // 将拟合后的值拿出来更新
      for (int i = 0; i < size; i++) {
        float value = (float) curveFitter.f(i); // 拟合后的值
        result.set(i, value);  // curveFitter.f(xData[i]) 根据第 i 个 x 值求拟合后的 y 值
      }
    }*/

    //新的拟合方法 0909新改先注释
    private static void newCurveFitter(List<Float> result, List<Float> origin, int startCycle, int endCycle) {
      float constValue = 0f;
      float average;
      float sum = 0f;
    /*  for (Float aFloat : result) {
        if (aFloat < 0F) {
        } else {
          constValue = aFloat;
          break;
        }
      }*/
      for (int i = startCycle; i <= endCycle; i++) {
//        if (result.get(i) < 0F) {
//          sum = sum + constValue;
//        } else {
        sum = sum + result.get(i);
//        }
      }
      average = sum / (endCycle - startCycle + 1);
      // 将拟合后的值拿出来更新
      for (int i = 0; i < result.size(); i++) {
//        if ((result.get(i) - average) < 0) {
//          result.set(i, 0.001F);
//        } else {
        result.set(i, average);  // curveFitter.f(xData[i]) 根据第 i 个 x 值求拟合后的 y 值
      }
    }

    /**
     * 四参数拟合
     *
     * @return 拟合后当前线的最小值
     */
    private static float curveFitter_4p(List<Float> result, List<Float> origin) {
      int size = result.size();
      double[] xData = new double[size];  // x 坐标数组
      double[] yData = new double[size];  // y 坐标数组
      for (int i = 0; i < size; i++) {
        xData[i] = i; // x 坐标数组
      }
      for (int i = 0; i < size; i++) {
        yData[i] = result.get(i); // y 坐标数组
      }
      CurveFitter curveFitter = new CurveFitter(xData, yData);    // 实例化
      float min = Float.MAX_VALUE, max = -Float.MAX_VALUE;
      for (double yDatum : yData) {
        if (min - yDatum > 0) {
          min = (float) yDatum;
        }
        if (max - yDatum < 0) {
          max = (float) yDatum;
        }
      }
      // 李老师提出的可能会使用的另外一种拟合公式
//       curveFitter.doCustomFit((double[] p, double x) -> p[0] + (p[3] - p[0]) / (1 + Math.pow(10, (p[2] - x) * p[1])), 4, "y = a+(d-a)/(1+10^((c-x)*b))", new double[]{min, 20, 35, max}, null, false);
      curveFitter.doCustomFit((p, x) -> p[3] + (p[0] - p[3]) / (1 + Math.pow(x / p[2], p[1])), 4, "y = d+(a-d)/(1+(x/c)^b)", new double[]{min, 20, 35, max}, null, false);
      min = Float.MAX_VALUE;
      // 将拟合后的值拿出来更新
      for (int i = 0; i < size; i++) {
//        float value = (float) curveFitter.f(i); // 拟合后的值
//        result.set(i, value);  // curveFitter.f(xData[i]) 根据第 i 个 x 值求拟合后的 y 值
//        if (min > value) {
//          min = value;   // 当前线的最小值
//        }

        float value = (float) curveFitter.f(i); // 拟合后的值
        value = (origin.get(i) + value * 2) / 3;
        result.set(i, value);  // curveFitter.f(xData[i]) 根据第 i 个 x 值求拟合后的 y 值
        if (min > value) {
          min = value;   // 当前线的最小值
        }
      }
      return min;
    }
  }
}
